package com.music.auth.service.impl;

import com.music.auth.form.LoginWebBody;
import com.music.auth.form.RegisterWebBody;
import com.music.auth.service.LoginService;
import com.music.common.core.constant.Constants;
import com.music.common.core.constant.SecurityConstants;
import com.music.common.core.domain.R;
import com.music.common.core.enums.ExceptionCode;
import com.music.common.core.exception.CustomException;
import com.music.common.core.utils.bean.BeanUtils;
import com.music.common.core.utils.sign.Md5Utils;
import com.music.common.core.utils.token.JwtTokenUtils;
import com.music.common.redis.service.RedisService;
import com.music.message.api.RemoteUserMessageService;
import com.music.user.api.RemoteUserService;
import com.music.user.domain.User;
import com.music.user.dto.UserDto;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

/**
 * Author 史豪通
 * Date 2024/10/27 16:58
 */
@Service
public class LoginServiceImpl implements LoginService {
    @Resource
    RemoteUserService remoteUserService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedisService redisService;
    @Resource
    private RemoteUserMessageService remoteUserMessageService;

    @Override
    public UserDto login(LoginWebBody loginWebBody) {
        String userAccount = loginWebBody.getUserAccount();
        // 使用布隆过滤器，判断用户是否存在于数据库中
        Long result = findUserAccount(userAccount);
        if (result.intValue() == 0) throw new CustomException(ExceptionCode.INVALID_USERNAME);

        // 限制次数
        limitUserGetInfo(Constants.USER_GETINFO_CNT, userAccount);

        // 2.验证登录
        User user = new User();
        user.setUserAccount(loginWebBody.getUserAccount());
        user.setUserPassword(Md5Utils.hash(Md5Utils.hash(loginWebBody.getUserPassword())));
        R<User> userResult = remoteUserService.getUserInfo(user.getUserAccount(), user.getUserPassword(), SecurityConstants.INNER);

        User login = userResult.getData();

        if (ObjectUtils.isEmpty(login)) {
            throw new CustomException(ExceptionCode.INVALID_PASSWORD);
        }
        // 校验用户状态
        if (Constants.UN_AVAILABLE_USER_STATUS.equals(login.getUserStatus())) {
            throw new CustomException(ExceptionCode.USER_STATUS_CHECK_ERROR);
        }

        UserDto userDto = new UserDto();
        BeanUtils.copyProperties(login, userDto, "userPassword", "userType", "userStatus");

        // 3.生成jwt
        String token = JwtTokenUtils.generateWebJwtToken(login.getUserId(), login.getUserName(), login.getUserAccount());
        userDto.setToken(token);

        // 存入redis
        redisService.setCacheObject(Constants.LOGIN_USER_TOKEN + userAccount, token, 7L, TimeUnit.DAYS);

        return userDto;
    }

    @Override
    public String register(RegisterWebBody registerWebBody) {
        // 校验短信验证码
        remoteUserMessageService.check(registerWebBody.getUserAccount(), registerWebBody.getSmsCode(), SecurityConstants.INNER);

        // 封装user
        User user = new User();
        BeanUtils.copyProperties(registerWebBody, user, "smsCode");
        user.setUserPhone(registerWebBody.getUserAccount());
        user.setUserName("用户" + registerWebBody.getUserAccount().substring(7));
        user.setUserType(2);
        user.setUserStatus(1);
        remoteUserService.add(user, SecurityConstants.INNER);
        return "";
    }

    public Long findUserAccount(String userAccount) {
        String script = "return tonumber(redis.call(\"bf.exists\", KEYS[1], ARGV[1]))";

        return stringRedisTemplate.opsForValue()
                .getOperations()
                .execute(new DefaultRedisScript<>(script, Long.class),
                        new ArrayList<String>() {{
                            add(Constants.WHITE_USER_ACCOUNT_KEY);
                        }},
                        userAccount);
    }

    // 限制次数
    public void limitUserGetInfo(String preKey, String userAccount) {
        String key = preKey + userAccount;
        // 增加请求计数
        Long cnt = stringRedisTemplate.opsForValue().increment(key, 1);

        // 设置键的过期时间（可选）
        if (cnt <= 4) {
            stringRedisTemplate.expire(key, 5, TimeUnit.MINUTES);
        }

        // 检查请求次数限制
        if (cnt > 3) {
            throw new CustomException(ExceptionCode.USER_GETINFO_LIMIT);
        }
    }
}
